package com.distribute.core.core;


import com.distribute.core.annotation.DLCCell;
import com.distribute.core.annotation.EnableDistributeLocalCache;
import com.distribute.core.constants.CacheConstants;
import com.distribute.core.core.el.DLCNotifyAspect;
import com.distribute.core.init.AbstractNotifyComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 本地缓存 自定义组件扫描 器
 *
 * @author chennengcheng
 * @date 2020-04-24 9:43
 * @remark
 */

public class DistributeLocalCacheScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

    private static final Logger log = LoggerFactory.getLogger(DistributeLocalCacheScannerRegistrar.class);
    /**
     * 资源
     */
    private ResourceLoader resourceLoader;

    /**
     * 类加载器
     */
    private ClassLoader classLoader;

    /**
     * 环境
     */
    private Environment environment;

    private BeanDefinitionRegistry registry;

    private AnnotationMetadata importingClassMetadata;

    public DistributeLocalCacheScannerRegistrar() {
    }

    @Override
    public void setResourceLoader(@NonNull ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setBeanClassLoader(@NonNull ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setEnvironment(@NonNull Environment environment) {
        this.environment = environment;
    }


    /**
     * 将 包含 TemplateService 的注解类纳为 spring bean 容器管理
     *
     * @param importingClassMetadata
     * @param registry
     */
    @Override
    public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) {
        this.registry = registry;
        this.importingClassMetadata = importingClassMetadata;
        init();
    }

    public void init(){
        Set<String> basePackages = getBasePackages();
        // 先初始化扫描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        // 设置资源加载
        scanner.setResourceLoader(this.resourceLoader);
        // 设置需要过滤的注解类
        scanner.addIncludeFilter(new AnnotationTypeFilter(DLCCell.class));
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            for (BeanDefinition candidateComponent : candidateComponents) {
                // 是否是注解的Bean
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    // 注册缓存单元
                    registerCacheComponent(this.registry, annotationMetadata);
                }
            }
        }

        // 实例化缓存组件
        initBaseConstants();
        // 实例化通知切面
        initAspectNotify();
    }


    public void initAspectNotify(){
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(DLCNotifyAspect.class);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.setBeanClass(DLCNotifyAspect.class);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        String beanId = DLCNotifyAspect.class.getSimpleName();
        // 使用类路径进行注册
        this.registry.registerBeanDefinition(beanId, definition);
    }

    @SuppressWarnings("all")
    public void initBaseConstants(){
        Map<String, Object> attributes = this.importingClassMetadata.getAnnotationAttributes(
                EnableDistributeLocalCache.class.getCanonicalName());
        Class<? extends AbstractNotifyComponent> notifyComponentClazz =
                (Class<? extends AbstractNotifyComponent>) attributes.get("notifyOfComponent");
        CacheConstants.notifyOfComponent = notifyComponentClazz;
        log.info("本地缓存通知更新插件：" + notifyComponentClazz.getSimpleName() + " 已加载！");
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(notifyComponentClazz);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.setBeanClass(notifyComponentClazz);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        String beanId = notifyComponentClazz.getSimpleName();
        // 使用类路径进行注册
        registry.registerBeanDefinition(beanId, definition);
    }

    /**
     * 相关包路径扫描
     *
     * @param importingClassMetadata
     * @return
     */
    @SuppressWarnings("all")
    protected Set<String> getBasePackages() {
        Map<String, Object> attributes = this.importingClassMetadata.getAnnotationAttributes(EnableDistributeLocalCache.class.getCanonicalName());
        Set<String> basePackages = new HashSet<>();
        for (String pkg : (String[]) attributes.get("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(this.importingClassMetadata.getClassName()));
        }

        return basePackages;
    }


    /**
     * 类路径扫描器
     *
     * @return
     */
    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().getInterfaceNames().length == 1
                            && Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {
                        try {
                            Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), DistributeLocalCacheScannerRegistrar.this.classLoader);
                            return !target.isAnnotation();
                        } catch (Exception ex) {
                            this.logger.error("Could not load target class: " + beanDefinition.getMetadata().getClassName(), ex);
                        }
                    }

                    return true;
                }

                return false;
            }
        };
    }

    /**
     * 注册模板组件 以类路径作为 bean 注册id，避免重名beanName冲突
     *
     * @param registry
     * @param annotationMetadata
     */
    private void registerCacheComponent(BeanDefinitionRegistry registry,
                                        AnnotationMetadata annotationMetadata) {
        try {
            String className = annotationMetadata.getClassName();
            Class<?> beanClazz = Class.forName(className);
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
            definition.setBeanClass(beanClazz);
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            componentInitAfterProcess(beanClazz, definition);
            // 使用类路径进行注册
            registry.registerBeanDefinition(beanClazz.getName(), definition);
        } catch (ClassNotFoundException e) {
            log.error("Could not register target class: {}, {}", annotationMetadata.getClassName(), e);
        }
    }


    /**
     * 组件初始化后处理
     * @param beanClazz
     * @param definition
     */
    private void componentInitAfterProcess(Class<?> beanClazz, GenericBeanDefinition definition){
        if (beanClazz.isAnnotationPresent(DLCCell.class)) {
            initPropertyValues(beanClazz, definition);
        }
    }

    /**
     * 初始化 BaseCacheCell 属性信息
     * @param beanClazz
     * @param definition
     */
    private void initPropertyValues(Class<?> beanClazz, GenericBeanDefinition definition){
        boolean isAnnotation = beanClazz.isAnnotationPresent(DLCCell.class);
        if (!isAnnotation) {
            return;
        }

        DLCCell cacheCell = beanClazz.getAnnotation(DLCCell.class);
        definition.getPropertyValues().add("modelPrefix", cacheCell.modelPrefix());
        definition.getPropertyValues().add("model", cacheCell.model());
        definition.getPropertyValues().add("status", cacheCell.status());
        definition.getPropertyValues().add("cacheClassSimpleName", beanClazz.getSimpleName());
        CacheManageContext.addCell(cacheCell.modelPrefix(), cacheCell.model(), beanClazz.getName());
        CacheConstants.CELLS_MAP.put(beanClazz.getName(), cacheCell);
    }

}
